package com.htdata.iiot.tools;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.List;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.lang.StringUtils;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;

public class ZkConfig {
	private static int TIME_OUT = 3000;

	public static String HELP = "help";
	public static String ZOOKEEPER = "zookeeper";
	public static String EXPORT = "export";
	public static String PATH = "path";
	public static String IMPORT = "import";

	private static ZooKeeper zookeeper = null;

	public static void zkExport(String parentPath, OutputStreamWriter outputStreamWriter)
			throws IOException, KeeperException, InterruptedException {
		Stat stat = zookeeper.exists(parentPath, false);
		int n = stat.getNumChildren();
		if (n > 0) {
			List<String> children = zookeeper.getChildren(parentPath, false);

			for (String child : children) {
				zkExport(parentPath + "/" + child, outputStreamWriter);
			}
		} else {
			byte[] pdata = zookeeper.getData(parentPath, false, null);
			if (pdata != null) {
				outputStreamWriter.write(parentPath);
				outputStreamWriter.write(" = ");
				outputStreamWriter.write(new String(pdata));
				outputStreamWriter.write(0x0A); // "\n"
			}
		}
	}

	public static void zkCreate(String path) throws KeeperException, InterruptedException {
		int n = path.lastIndexOf('/');
		if (n > 0) {
			String parent = path.substring(0, n);
			if (zookeeper.exists(parent, false) == null) {
				zkCreate(parent);
			}
		}

		zookeeper.create(path, "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
	}

	public static void zkImport(InputStreamReader inputStreamReader)
			throws IOException, KeeperException, InterruptedException {
		BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
		int pos = 0;
		int len = 0;
		String[] split = new String[2];

		while (true) {
			String line = bufferedReader.readLine();
			if (line == null) {
				break;
			}

			len = line.length();
			pos = line.indexOf('=');
			if (-1 == pos) {
				continue;
			}

			split[0] = line.substring(0, pos);
			if (pos < len - 1) {
				split[1] = line.substring(pos + 1);
			} else {
				split[1] = "";
			}

			String path = split[0].trim();
			String value = split[1].trim();

			if (path.length() == 0 || value.length() == 0) {
				continue;
			}

			if (!path.startsWith("/")) {
				continue;
			}

			if (zookeeper.exists(path, false) != null) {
				zookeeper.setData(path, value.getBytes(), -1);
			} else {
				String parent = path.substring(0, path.lastIndexOf('/'));
				if (zookeeper.exists(parent, false) == null) {
					zkCreate(parent);
				}

				zookeeper.create(path, value.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
			}
		}
	}

	public static void usage(Options options) {
		HelpFormatter helpFormatter = new HelpFormatter();
		helpFormatter.setOptionComparator(null);
		helpFormatter.setWidth(120);
		
		//options with null caused exception, have to use sysout function
		//helpFormatter.printHelp("./zkConfig -z <ip:port> -e <filename> [-p <zkpath>]", null);
		System.out.print(helpFormatter.getSyntaxPrefix());
		System.out.println("./zkConfig -z <ip:port> -e <filename> [-p <zkpath>]");
		
		String prefix = StringUtils.repeat(" ", helpFormatter.getSyntaxPrefix().length());
		helpFormatter.setSyntaxPrefix(prefix);
		helpFormatter.printHelp("./zkConfig -z <ip:port> -i <filename>", options);
	}

	public static void main(String[] args) throws Exception {
		String zkAddr = null;
		String zkExport = null;
		String zkPath = null;
		String zkImport = null;

		Options optionsH = new Options();
		optionsH.addOption("h", ZkConfig.HELP, false, "show help information");

		Options options = new Options();
		options.addRequiredOption("z", ZkConfig.ZOOKEEPER, true, "information of zookeeper, format: ip:port");
		options.addOption("e", ZkConfig.EXPORT, true, "filename of exporting");
		options.addOption("p", ZkConfig.PATH, true, "absolute path in zookeeper");
		options.addOption("i", ZkConfig.IMPORT, true, "filename of importing");

		if (args.length == 0) {
			usage(options);
			return;
		}

		CommandLine commandLine = null;
		CommandLineParser parser = null;
		parser = new DefaultParser();

		try {
			commandLine = parser.parse(optionsH, args);
			if (commandLine.hasOption('h')) {
				usage(options);
				return;
			}
		} catch (Exception e) {
		}

		try {
			commandLine = parser.parse(options, args);
			zkAddr = commandLine.getOptionValue(ZkConfig.ZOOKEEPER);
			zkExport = commandLine.getOptionValue(ZkConfig.EXPORT);
			zkPath = commandLine.getOptionValue(ZkConfig.PATH);
			zkImport = commandLine.getOptionValue(ZkConfig.IMPORT);

			zkAddr = zkAddr.trim();

			if (zkExport != null) {
				zkExport = zkExport.trim();

				if (zkPath != null) {
					zkPath = zkPath.trim();
				}
			} else {
				if (zkImport != null) {
					zkImport = zkImport.trim();
				}
			}
		} catch (Exception e) {
			usage(options);
			return;
		}


		if (zkPath == null || zkPath.length() == 0) {
			zkPath = "/conf_htiiot";
		}

		try {
			zookeeper = new ZooKeeper(zkAddr, TIME_OUT, null);

			if (zkExport != null) {
				FileOutputStream fileOutputStream = new FileOutputStream(zkExport);
				OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream);
				zkExport(zkPath, outputStreamWriter);
				outputStreamWriter.close();
				fileOutputStream.close();
				System.out.println("exported successfully");
			} else if (zkImport != null) {
				File file = new File(zkImport);
				if(!file.exists()){
					System.out.println("`" + zkImport + "` not exists!");
					return;
				}
				
				if(!file.isFile()){
					System.out.println("`" + zkImport + "` is not a regular file!");
					return;
				}
				
				FileInputStream fileInputStream = new FileInputStream(file);
				InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream);
				zkImport(inputStreamReader);
				System.out.println("imported successfully");
			}
		} catch (Exception e) {
			System.out.println(e.getMessage());
			e.printStackTrace();
		} finally {
			if (zookeeper != null) {
				zookeeper.close();
			}
		}
	}
}
